﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;

namespace Sparrow.Reflection
{
    public class Proxy : DynamicObject
    {
        public Proxy(object instance)
        {
            this.Instance = instance;
            this._Methods = new Lazy<MethodProxy>(() => new MethodProxy(this.Instance));
            this._Properties = new Lazy<PropertyProxy>(() => new PropertyProxy(this.Instance));
            this._Fields = new Lazy<FieldProxy>(() => new FieldProxy(this.Instance));
        }

        protected object Instance
        {
            get;
            set;
        }

        Lazy<MethodProxy> _Methods;
        public MethodProxy Methods
        {
            get
            {
                return this._Methods.Value;
            }
        }

        Lazy<PropertyProxy> _Properties;
        public PropertyProxy Properties
        {
            get
            {
                return this._Properties.Value;
            }
        }

        Lazy<FieldProxy> _Fields;
        public FieldProxy Fields
        {
            get
            {

                return this._Fields.Value;
            }
        }

        public object this[string name]
        {
            get
            {
                var property = this.Instance.Metadata().Properties[name];

                if (property != null)
                {
                    return property.GetValue(this.Instance);

                }

                var field = this.Instance.Metadata().Fields[name];

                if (field != null)
                {
                    return field.GetValue(this.Instance);

                }

                return null;
            }
            set
            {
                var property = this.Instance.Metadata().Properties[name];

                if (property != null)
                {
                    property.SetValue(this.Instance, value);
                }
            }
        }

        public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
        {
            result = this[binder.Name];
            return result != null;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            this[binder.Name] = value;
            return true;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            result = null;
            try
            {
                var method = this.Instance.Metadata().Methods[binder.Name];

                if (method != null)
                {
                    result = method.Invoke(this.Instance, args);
                    return true;
                }

                return false;
            }
            catch
            {
                return false;
            }


        }

        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
        {
            return base.TryInvoke(binder, args, out result);
        }
    }
}
